/*
 *    This file is part of SSHTunneler.
 *
 *    Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
 *
 *    SSHTunneler is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    SSHTunneler is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with SSHTunneler.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package sshtunneler.client.data;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

import sshtunneler.client.SSHTunnelerApp;
import sshtunneler.library.DefaultPort;
import sshtunneler.library.DefaultPorts;
import sshtunneler.library.ExtensionFilter;

public class Config {

	public final static Config inst = new Config();

	/*** Options ***/
	public static String default_username = "";
	public static Boolean auto_save_options = false;
	public static Boolean auto_save_servers_and_tunnel = false;
	public static Boolean start_minimized = false;
	public static Boolean connect_to_all_on_start = false;
	public static Boolean hide_tool_config = false;

	/*** File Information ***/
	public static String extension = ".ssc";
	public static ExtensionFilter filter;

	/*** XML Information ***/
	private static String ROOT_ELEMENT_NAME = "SSHTunnelerConfig";
	private static String DEFAULT_USERNAME_ELEMENT_NAME = "DefaultUsername";
	private static String AUTO_SAVE_OPTIONS_ELEMENT_NAME = "AutoSaveOptions";
	private static String AUTO_SAVE_SERVERS_TUNNELS_ELEMENT_NAME = "AutoSaveServersAndTunnels";
	private static String START_MINIMIZED_ELEMENT_NAME = "StartMinimized";
	private static String CONNECT_TO_ALL_ON_START_ELEMENT_NAME = "ConnectToAllOnStart";
	private static String PORT_CONFIG_ELEMENT_NAME = "PortConfig";
	private static String PORT_INFO_ELEMENT_NAME = "PortInfo";
	private static String PORT_ELEMENT_NAME = "Port";
	private static String PORT_DESCRIPTION_ELEMENT_NAME = "Description";
	private static String HIDE_TOOL_CONFIG_ELEMENT_NAME = "HideToolsConfig";

	/*** Instance ***/
	private Document document;
	private Element root;

	{
		filter = new ExtensionFilter("SSH Tunneler Config File");
		filter.add_accepted_extension(extension);
	}

	private Config() {
	}

	public void auto_save() {
		try {
			Config.inst.save(Config.inst.getDefaultFile(false));
		} catch (IOException ex) {
			Logger.getLogger(SSHTunnelerApp.class.getName()).log(Level.SEVERE,
					null, ex);
		}
	}

	public void auto_load() {
		try {
			boolean loaded = Config.inst.load(Config.inst.getDefaultFile(true));
			if (loaded) {
				Logger.getLogger(SSHTunnelerApp.class.getName()).log(
						Level.INFO, "Loaded default config");
			}
		} catch (JDOMException ex) {
			Logger.getLogger(SSHTunnelerApp.class.getName()).log(Level.SEVERE,
					null, ex);
		} catch (IOException ex) {
			Logger.getLogger(SSHTunnelerApp.class.getName()).log(Level.SEVERE,
					null, ex);
		}
	}

	private void buildDocumentFromConfig() {
		this.root = new Element(ROOT_ELEMENT_NAME);
		this.document = new Document(this.root);

		{
			Element elem = new Element(DEFAULT_USERNAME_ELEMENT_NAME);
			elem.setText(Config.default_username);
			this.root.addContent(elem);
		}
		{
			Element elem = new Element(AUTO_SAVE_OPTIONS_ELEMENT_NAME);
			elem.setText(Config.auto_save_options.toString());
			this.root.addContent(elem);
		}
		{
			Element elem = new Element(AUTO_SAVE_SERVERS_TUNNELS_ELEMENT_NAME);
			elem.setText(Config.auto_save_servers_and_tunnel.toString());
			this.root.addContent(elem);
		}
		{
			Element elem = new Element(START_MINIMIZED_ELEMENT_NAME);
			elem.setText(Config.start_minimized.toString());
			this.root.addContent(elem);
		}
		{
			Element elem = new Element(CONNECT_TO_ALL_ON_START_ELEMENT_NAME);
			elem.setText(Config.connect_to_all_on_start.toString());
			this.root.addContent(elem);
		}
		{
			Element elem = new Element(HIDE_TOOL_CONFIG_ELEMENT_NAME);
			elem.setText(Config.hide_tool_config.toString());
			this.root.addContent(elem);
		}
		{
			Element elem = new Element(PORT_CONFIG_ELEMENT_NAME);
			this.root.addContent(elem);

			for (DefaultPort p : DefaultPorts.inst.get_all_ports()) {
				Element sub_elem = new Element(PORT_INFO_ELEMENT_NAME);
				elem.addContent(sub_elem);

				sub_elem.setAttribute(PORT_ELEMENT_NAME, "" + p.getPort());
				sub_elem.setAttribute(PORT_DESCRIPTION_ELEMENT_NAME, p
						.getName());
			}
		}
	}

	private void resetCurrentConfig() {
		Config.default_username = "";
		Config.auto_save_options = false;
		Config.auto_save_servers_and_tunnel = false;
		Config.start_minimized = false;

		DefaultPorts.inst.clear();
	}

	private void buildConfigFromDocument() {
		{
			Element elem = this.root.getChild(DEFAULT_USERNAME_ELEMENT_NAME);
			if (elem != null) {
				Config.default_username = elem.getText();
			}
		}
		{
			Element elem = this.root.getChild(AUTO_SAVE_OPTIONS_ELEMENT_NAME);
			if (elem != null) {
				Config.auto_save_options = Boolean.parseBoolean(elem.getText());
			}
		}
		{
			Element elem = this.root
					.getChild(AUTO_SAVE_SERVERS_TUNNELS_ELEMENT_NAME);
			if (elem != null) {
				Config.auto_save_servers_and_tunnel = Boolean.parseBoolean(elem
						.getText());
			}
		}
		{
			Element elem = this.root.getChild(START_MINIMIZED_ELEMENT_NAME);
			if (elem != null) {
				Config.start_minimized = Boolean.parseBoolean(elem.getText());
			}
		}
		{
			Element elem = this.root
					.getChild(CONNECT_TO_ALL_ON_START_ELEMENT_NAME);
			if (elem != null) {
				Config.connect_to_all_on_start = Boolean.parseBoolean(elem
						.getText());
			}
		}
		{
			Element elem = this.root.getChild(HIDE_TOOL_CONFIG_ELEMENT_NAME);
			if (elem != null) {
				Config.hide_tool_config = Boolean.parseBoolean(elem.getText());
			}
		}
		Element root_ports = this.root.getChild(PORT_CONFIG_ELEMENT_NAME);
		if (root_ports != null) {
			Iterator<?> i = root_ports.getChildren(PORT_INFO_ELEMENT_NAME)
					.iterator();
			while (i.hasNext()) {
				Element xml_port = (Element) i.next();

				String str_port = xml_port.getAttributeValue(PORT_ELEMENT_NAME);
				String str_descr = xml_port
						.getAttributeValue(PORT_DESCRIPTION_ELEMENT_NAME);

				int port;

				try {
					port = Integer.parseInt(str_port);
				} catch (NumberFormatException e) {
					port = 0;
				}
				DefaultPorts.inst.add_port(str_descr, port);
			}
		}
	}

	public void save(File file) throws IOException {
		this.buildDocumentFromConfig();

		XMLOutputter writter = new XMLOutputter(Format.getPrettyFormat());
		writter.output(this.document, new FileOutputStream(file));
	}

	public File getDefaultFile(boolean must_exist) {
		String home_path = System.getProperty("user.home");
		String default_file_name = "SSHTunneler_Default" + Config.extension;
		if (home_path == null) {
			return null;
		}

		if (!home_path.endsWith(File.separator)) {
			home_path = home_path + File.separator;
		}

		String file_path = home_path + default_file_name;
		File file = new File(file_path);

		if (must_exist && !file.exists()) {
			return null;
		}
		return file;
	}

	public boolean load(File file) throws JDOMException, IOException {
		if (file == null) {
			return false;
		}
		SAXBuilder sxb = new SAXBuilder();
		this.document = sxb.build(file);
		this.root = this.document.getRootElement();

		this.resetCurrentConfig();
		this.buildConfigFromDocument();
		return true;
	}
}
